#include "./BSP/LCD/bsp_lcd.h"
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>

/* lcd_ex.c存放各个LCD驱动IC的寄存器初始化部分代码,以简化lcd.c,该.c文件
 * 不直接加入到工程里面,只有lcd.c会用到,所以通过include的形式添加.(不要在
 * 其他文件再包含该.c文件!!否则会报错!)
 */
#include "./BSP/LCD/bsp_lcd_ex.c"

/* LCD的画笔颜色和背景色 */
uint32_t g_point_color = 0XF800; /* 画笔颜色 */
uint32_t g_back_color = 0XFFFF;  /* 背景色 */

/* 管理LCD重要参数 */
_lcd_dev lcddev;

/**
 * @brief       LCD写数据
 * @param       data: 要写入的数据
 * @retval      无
 */
void lcd_wr_data(volatile uint16_t data) {
  data = data; /* 使用-O2优化的时候,必须插入的延时 */
  LCD->LCD_RAM = data;
}

/**
 * @brief       LCD写寄存器编号/地址函数
 * @param       regno: 寄存器编号/地址
 * @retval      无
 */
void lcd_wr_regno(volatile uint16_t regno) {
  regno = regno;        /* 使用-O2优化的时候,必须插入的延时 */
  LCD->LCD_REG = regno; /* 写入要写的寄存器序号 */
}

/**
 * @brief       LCD写寄存器
 * @param       regno:寄存器编号/地址
 * @param       data:要写入的数据
 * @retval      无
 */
void lcd_write_reg(uint16_t regno, uint16_t data) {
  LCD->LCD_REG = regno; /* 写入要写的寄存器序号 */
  LCD->LCD_RAM = data;  /* 写入数据 */
}

/**
 * @brief       LCD延时函数,仅用于部分在mdk -O1时间优化时需要设置的地方
 * @param       t:延时的数值
 * @retval      无
 */
static void lcd_opt_delay(uint32_t i) {
  while (i--)
    ;
}

/**
 * @brief       LCD读数据
 * @param       无
 * @retval      读取到的数据
 */
static uint16_t lcd_rd_data(void) {
  volatile uint16_t ram; /* 防止被优化 */
  lcd_opt_delay(2);
  ram = LCD->LCD_RAM;
  return ram;
}

/**
 * @brief       准备写GRAM
 * @param       无
 * @retval      无
 */
void lcd_write_ram_prepare(void) { LCD->LCD_REG = lcddev.wramcmd; }

/**
 * @brief       设置光标位置(对RGB屏无效)
 * @param       x,y: 坐标
 * @retval      无
 */
void lcd_set_cursor(uint16_t x, uint16_t y) {
  if (lcddev.id == 0X1963)
  {
    if (lcddev.dir == 0) /* 竖屏模式, x坐标需要变换 */
    {
      x = lcddev.width - 1 - x;
      lcd_wr_regno(lcddev.setxcmd);
      lcd_wr_data(0);
      lcd_wr_data(0);
      lcd_wr_data(x >> 8);
      lcd_wr_data(x & 0XFF);
    }
    else /* 横屏模式 */
    {
      lcd_wr_regno(lcddev.setxcmd);
      lcd_wr_data(x >> 8);
      lcd_wr_data(x & 0XFF);
      lcd_wr_data((lcddev.width - 1) >> 8);
      lcd_wr_data((lcddev.width - 1) & 0XFF);
    }

    lcd_wr_regno(lcddev.setycmd);
    lcd_wr_data(y >> 8);
    lcd_wr_data(y & 0XFF);
    lcd_wr_data((lcddev.height - 1) >> 8);
    lcd_wr_data((lcddev.height - 1) & 0XFF);
  }
  else if (lcddev.id == 0X5510)
  {
    lcd_wr_regno(lcddev.setxcmd);
    lcd_wr_data(x >> 8);
    lcd_wr_regno(lcddev.setxcmd + 1);
    lcd_wr_data(x & 0XFF);
    lcd_wr_regno(lcddev.setycmd);
    lcd_wr_data(y >> 8);
    lcd_wr_regno(lcddev.setycmd + 1);
    lcd_wr_data(y & 0XFF);
  }
  else /* 9341/5310/7789/7796/9806 等 设置坐标 */
  {
    lcd_wr_regno(lcddev.setxcmd);
    lcd_wr_data(x >> 8);
    lcd_wr_data(x & 0XFF);
    lcd_wr_regno(lcddev.setycmd);
    lcd_wr_data(y >> 8);
    lcd_wr_data(y & 0XFF);
  }
}

/**
 * @brief  读取LCD驱动的ID
 * @note
 * @param
 * @retval
 */
void lcd_driver_ID_Read(void) {
  /**
   * 显示一下LCD的命令和数据地址
   * 特别注意, 如果在main函数里面屏蔽串口1初始化, 则会卡死在printf里面(卡死在f_putc函数),
   * 所以, 必须初始化串口1, 或者屏蔽掉下面 的printf 语句 !!!!!!!
   */
  // 基础地址信息
  printf("LCD_BASE: 0x%08X\r\n", LCD_BASE);
  printf("LCD     : 0x%08X\r\n", (uint32_t)LCD);
  // 寄存器地址信息
  printf("LCD_REG : 0x%08X\r\n", (uint32_t)&LCD->LCD_REG);
  printf("LCD_RAM : 0x%08X\r\n", (uint32_t)&LCD->LCD_RAM);
  /* 尝试9341 ID的读取 */
  lcd_wr_regno(0XD3);
  lcddev.id = lcd_rd_data(); /* dummy read */
  lcddev.id = lcd_rd_data(); /* 读到0X00 */
  lcddev.id = lcd_rd_data(); /* 读取0X93 */
  lcddev.id <<= 8;
  lcddev.id |= lcd_rd_data(); /* 读取0X41 */

  printf("LCD driver id:0x%X\r\n", lcddev.id);
}

/***********************************************************
---------------------------------------------------------
                  LCD屏示例
              |-----------------|
              |                 |
              |                 |
              |                 |
              |                 |
              |                 |
              |                 |
              |                 |
              |                 |
              |                 |
              |-----------------|
              屏幕正面（宽240，高320）
 *******************************************************/

/**
 * @brief  设置ILI9341的GRAM的扫描方向
 * @note   坐标图例：o表示(0,0)，^表示向上，V表示向下，<表示向左，>表示向右，X表示X轴，Y表示Y轴
 * @param  ucOption 选择GRAM的扫描方向
 *         @arg 0-7,参数可选值为0-7这八个方向
 *  	           ！！！其中0、3、5、6 模式适合从左至右显示文字，
 *				       不推荐使用其它模式显示文字	其它模式显示文字会有镜像效果
 *	             其中0、2、4、6 模式的X方向像素为240，Y方向像素为320
 *	             其中1、3、5、7 模式下X方向像素为320，Y方向像素为240
 * @retval
 */
void lcd_scan_dir(uint8_t dir) {
  uint16_t regval = 0;
  uint16_t dirreg = 0;
  uint16_t temp;

  /* 横屏时，对1963不改变扫描方向, 其他IC改变扫描方向！竖屏时1963改变方向, 其他IC不改变扫描方向 */
  if ((lcddev.dir == 1 && lcddev.id != 0X1963) || (lcddev.dir == 0 && lcddev.id == 0X1963))
  {
    switch (dir) /* 方向转换 */
    {
      case 0:
        dir = 6;
        break;

      case 1:
        dir = 7;
        break;

      case 2:
        dir = 4;
        break;

      case 3:
        dir = 5;
        break;

      case 4:
        dir = 1;
        break;

      case 5:
        dir = 0;
        break;

      case 6:
        dir = 3;
        break;

      case 7:
        dir = 2;
        break;
    }
  }

  /* 根据扫描方式 设置 0X36/0X3600 寄存器 bit 5,6,7 位的值 */
  switch (dir)
  {
    case L2R_U2D: /* 从左到右,从上到下 */
      regval |= (0 << 7) | (0 << 6) | (0 << 5);
      break;

    case L2R_D2U: /* 从左到右,从下到上 */
      regval |= (1 << 7) | (0 << 6) | (0 << 5);
      break;

    case R2L_U2D: /* 从右到左,从上到下 */
      regval |= (0 << 7) | (1 << 6) | (0 << 5);
      break;

    case R2L_D2U: /* 从右到左,从下到上 */
      regval |= (1 << 7) | (1 << 6) | (0 << 5);
      break;

    case U2D_L2R: /* 从上到下,从左到右 */
      regval |= (0 << 7) | (0 << 6) | (1 << 5);
      break;

    case U2D_R2L: /* 从上到下,从右到左 */
      regval |= (0 << 7) | (1 << 6) | (1 << 5);
      break;

    case D2U_L2R: /* 从下到上,从左到右 */
      regval |= (1 << 7) | (0 << 6) | (1 << 5);
      break;

    case D2U_R2L: /* 从下到上,从右到左 */
      regval |= (1 << 7) | (1 << 6) | (1 << 5);
      break;
  }

  dirreg = 0X36; /* 对绝大部分驱动IC, 由0X36寄存器控制 */

  if (lcddev.id == 0X5510)
  {
    dirreg = 0X3600; /* 对于5510, 和其他驱动ic的寄存器有差异 */
  }

  /* 9341 & 7789 & 7796 要设置BGR位 */
  if (lcddev.id == 0X9341 || lcddev.id == 0X7789 || lcddev.id == 0x7796)
  {
    regval |= 0X08;
  }

  lcd_write_reg(dirreg, regval);

  if (lcddev.id != 0X1963) /* 1963不做坐标处理 */
  {
    if (regval & 0X20)
    {
      if (lcddev.width < lcddev.height) /* 交换X,Y */
      {
        temp = lcddev.width;
        lcddev.width = lcddev.height;
        lcddev.height = temp;
      }
    }
    else
    {
      if (lcddev.width > lcddev.height) /* 交换X,Y */
      {
        temp = lcddev.width;
        lcddev.width = lcddev.height;
        lcddev.height = temp;
      }
    }
  }

  /* 设置显示区域(开窗)大小 */
  if (lcddev.id == 0X5510)
  {
    lcd_wr_regno(lcddev.setxcmd);
    lcd_wr_data(0);
    lcd_wr_regno(lcddev.setxcmd + 1);
    lcd_wr_data(0);
    lcd_wr_regno(lcddev.setxcmd + 2);
    lcd_wr_data((lcddev.width - 1) >> 8);
    lcd_wr_regno(lcddev.setxcmd + 3);
    lcd_wr_data((lcddev.width - 1) & 0XFF);
    lcd_wr_regno(lcddev.setycmd);
    lcd_wr_data(0);
    lcd_wr_regno(lcddev.setycmd + 1);
    lcd_wr_data(0);
    lcd_wr_regno(lcddev.setycmd + 2);
    lcd_wr_data((lcddev.height - 1) >> 8);
    lcd_wr_regno(lcddev.setycmd + 3);
    lcd_wr_data((lcddev.height - 1) & 0XFF);
  }
  else
  {
    lcd_wr_regno(lcddev.setxcmd);
    lcd_wr_data(0);
    lcd_wr_data(0);
    lcd_wr_data((lcddev.width - 1) >> 8);
    lcd_wr_data((lcddev.width - 1) & 0XFF);
    lcd_wr_regno(lcddev.setycmd);
    lcd_wr_data(0);
    lcd_wr_data(0);
    lcd_wr_data((lcddev.height - 1) >> 8);
    lcd_wr_data((lcddev.height - 1) & 0XFF);
  }
}

/**
 * @brief       设置LCD显示方向
 * @param       dir:0,竖屏; 1,横屏
 * @retval      无
 */
void lcd_display_dir(uint8_t dir) {
  lcddev.dir = dir; /* 竖屏/横屏 */

  if (dir == 0) /* 竖屏 */
  {
    lcddev.width = 240;
    lcddev.height = 320;
    lcddev.wramcmd = 0X2C;
    lcddev.setxcmd = 0X2A;
    lcddev.setycmd = 0X2B;
  }
  else /* 横屏 */
  {
    lcddev.width = 320;  /* 默认宽度 */
    lcddev.height = 240; /* 默认高度 */

    lcddev.wramcmd = 0X2C;
    lcddev.setxcmd = 0X2A;
    lcddev.setycmd = 0X2B;
  }

  lcd_scan_dir(DFT_SCAN_DIR); /* 默认扫描方向 */
}

/**
 * @brief       清屏函数
 * @param       color: 要清屏的颜色
 * @retval      无
 */
void lcd_clear(uint16_t color) {
  uint32_t index = 0;
  uint32_t totalpoint = lcddev.width;
  totalpoint *= lcddev.height;  /* 得到总点数 */
  lcd_set_cursor(0x00, 0x0000); /* 设置光标位置 */
  lcd_write_ram_prepare();      /* 开始写入GRAM */

  for (index = 0; index < totalpoint; index++)
  {
    LCD->LCD_RAM = color;
  }
}

void LCD_init(void) {
  // 读取id
  lcd_driver_ID_Read();
  if (lcddev.id == 0X9341)
  {
    lcd_ex_ili9341_reginit(); /* 执行ILI9341初始化 */
  }

  lcd_display_dir(0); /* 默认为竖屏 */
  LCD_BL(1);          /* 点亮背光 */
  lcd_clear(WHITE);
}

void lcd_clear_test(void) {
  uint16_t color[] = {WHITE, BLACK, RED,      GREEN,     BLUE,     MAGENTA,    YELLOW, CYAN,      BROWN,
                      BRRED, GRAY,  DARKBLUE, LIGHTBLUE, GRAYBLUE, LIGHTGREEN, LGRAY,  LGRAYBLUE, LBBLUE};
  int      i = 0;
  for (i = 0; i < sizeof(color) / sizeof(color[0]); i++)
  {
    lcd_clear(color[i]);
    HAL_Delay(1000);
  }
}
void LCD_Func_Test(void) {
  LCD_init();
  lcd_clear_test();
}
